<cfcomponent>
	
	<cffunction name="getMode" access="public" returntype="string">
		<!--- 
			Set up dev as local host entry (/Windows/sys32/drivers/etc/host) using convention.
			
			Consider:
			1) no .com/org
			2) "dev" in host name
			
		 --->
		<cfif cgi.HTTP_HOST eq "csac" or cgi.HTTP_HOST eq "calgrants">
			<cfreturn "dev" />
		<cfelse>
			<cfreturn "prod" />
		</cfif>
	</cffunction>
	
	<cffunction name="getDatasource" access="public" returntype="string">
		<!--- 
			Consider one of the following:		
			1) Create application for com directory and use application variable
			2) Use same name for dev and prod datasources
			3) Name datasources from http_host
		
		<cfif getMode() eq "dev">
			<cfreturn "CSAC_Dev" />
		<cfelse>
			<cfreturn "CSAC" />
		</cfif> --->
		<cfreturn "CSAC" />
	</cffunction>
	
	<cffunction name="getValidator" access="public" returntype="any">
		<cfset var validator = "" />
		<cfif IsDefined("request.validator")>
			<cfreturn request.validator />
		<cfelseif IsDefined("application.validator")>
			<cfreturn application.validator />
		<cfelse>
			<cfreturn new validateThis.ValidateThis({extraRuleValidatorComponentPaths = "#GetDirectoryFromPath(ExpandPath('*'))#\validators\", definitionPath = "#GetDirectoryFromPath(ExpandPath('*'))#\bo\"}) />
		</cfif>
	</cffunction>
	
	<cffunction name="titleCase" access="public" returntype="string">
		<cfargument name="str" type="string" required="yes" />
		<cfscript>
		   var j = 1; 
		   var m = 1;
		   var doCap = true;
		   var tempVar = "";
		   /* Make each word in text an array variable */
		   var Words = ListToArray(LCase(Trim(arguments.str)), " ");
		   var excludeWords = StructNew();
		   var ResetToALLCAPS = StructNew();
		   /* Words to never capitalize */
		   //tempVar =  ListToArray("a,above,after,ain't,among,an,and,as,at,below,but,by,can't,don't,for,from,from,if,in,into,it's,nor,of,off,on,on,onto,or,over,since,the,to,under,until,up,with,won't");
		   tempVar = ListToArray("of,and");
		   for(j=1; j LTE (ArrayLen(tempVar)); j = j+1){
				   excludeWords[tempVar[j]] = 0;
		   }
		   /* Words to always capitalize */
		   tempVar = ListToArray("II,III,IV,V,VI,VII,VIII,IX,X,XI,XII,XIII,XIV,XV,XVI,XVII,XVIII,XIX,XX,XXI");
		   for(j=1; j LTE (ArrayLen(tempVar)); j = j+1){
				   ResetToALLCAPS[tempVar[j]] = 0;
		   }
		   /* Check words against exclude list */
		   for(j=1; j LTE (ArrayLen(Words)); j = j+1){
				   doCap = true;
				   /* Word must be less than four characters to be in the list of excluded words */
				   if(LEN(Words[j]) LT 4){
						   if(StructKeyExists(excludeWords,Words[j])){ doCap = false; }
				   }
				   /* Capitalize hyphenated words */
				   if(ListLen(trim(Words[j]),"-") GT 1){
						   for(m=2; m LTE ListLen(Words[j], "-"); m=m+1){
								   tempVar = ListGetAt(Words[j], m, "-");
								   tempVar = UCase(Mid(tempVar,1, 1)) & Mid(tempVar,2, Len(tempVar)-1);
								   Words[j] = ListSetAt(Words[j], m, tempVar, "-");
						   }
				   }
				   /* Automatically capitalize first and last words */
				   if(j eq 1 or j eq ArrayLen(Words)){ doCap = true; }
				   /* Capitalize qualifying words */
				   if(doCap){ Words[j] = UCase(Mid(Words[j],1, 1)) & Mid(Words[j],2, Len(Words[j])-1); }
				   if (structKeyExists(ResetToALLCAPS, Words[j])) Words[j] = UCase(Words[j]);
		   }
		   return ArrayToList(Words, " ");
		</cfscript>
	</cffunction>

	<cffunction name="uploadFormFile" access="public" returntype="any">
		<cfargument name="fileField" type="string" required="yes" />
		<cfargument name="destination" type="string" required="no" default="#request.tempDirectory#" />
		<cfset var uploadedFile = "" />
		<!--- ensure destination directory exists --->
		<!--- <cfset var filePathPattern = "^[a-zA-Z]:(\\[a-zA-Z0-9_]+)+$" />
		IsValid("regex", arguments[arguments.fileField], filePathPattern) and  --->
		<cfif NOT DirectoryExists(arguments.destination)>
			<cfdirectory action="create" directory="#arguments.destination#">
		</cfif>
		<!--- although it is not listed in the arguments, this function requires an argument that is named the value of arguments.fileField and is a form.input.file type --->
		<cffile action="upload" destination="#arguments.destination#" filefield="#arguments.fileField#" nameconflict="makeunique">
		<cfreturn cffile/>
	</cffunction>

	<cffunction name="passbackToQueryString" access="public" returntype="string">
		<cfargument name="passbackString" type="string" required="false" default="" />
		<cfset var queryString = "" />
		<cfset var loopIndex = 1 />
		<cfif Len(Trim(arguments.passbackString)) gt 0>
			<cfloop index="kvp" list="#arguments.passbackString#" delimiters=";">
				<cfif loopIndex gt 1>
					<cfset queryString = queryString & "&" />
				</cfif>
				<cfset queryString = queryString & ListFirst(kvp) & "=" & ListLast(kvp) /> 
			</cfloop>
			<cfset loopIndex = loopIndex + 1 />
		</cfif>
		<cfreturn queryString />
	</cffunction>
	
	<cffunction name="getFilters" access="public" returntype="struct">
		<cfargument name="ui" type="string" required="yes" />
		<cfargument name="filtersList" type="string" required="yes" />
		<cfargument name="filters" type="any" required="no" default="" hint="Can be a populated struct (applies filters), an empty struct (clears filters) or null (retrieves filters from client scope)" />
		<cfset var filter = "" />
		<cfset var filtersStruct = StructNew() />
		<cfset var clientFilters = StructNew() />
		<cfset var filtersWDDX = "" />
		<!--- Apply filters --->
		<cfif IsStruct(arguments.filters) and NOT StructIsEmpty(arguments.filters)>
			<cfloop index="filter" list="#arguments.filtersList#">
				<cfif StructKeyExists(arguments.filters, filter)>
					<cfset filtersStruct[filter] = arguments.filters[filter] />
				<cfelse>
					<cfset filtersStruct[filter] = "All" />
				</cfif>
			</cfloop>	
		<!--- Clear filters --->
		<cfelseif IsStruct(arguments.filters) and StructIsEmpty(arguments.filters)>
			<cfloop index="filter" list="#arguments.filtersList#">
				<cfset filtersStruct[filter] = "All" />
			</cfloop>
		<!--- Retrieve filters --->
		<cfelseif NOT IsStruct(arguments.filters)>
			<cfif StructKeyExists(client, arguments.ui & ".filtersWDDX")>
				<cfset filtersWDDX = client[arguments.ui & ".filtersWDDX"] />
				<cfwddx action="wddx2cfml" input="#filtersWDDX#" output="clientFilters" />
			</cfif>
			<cfloop index="filter" list="#arguments.filtersList#">
				<cfif StructKeyExists(clientFilters, filter)>
					<cfset filtersStruct[filter] = clientFilters[filter] />
				<cfelse>
					<cfset filtersStruct[filter] = "All" />
				</cfif>
			</cfloop>
		</cfif>
		<cfwddx action="cfml2wddx" input="#filtersStruct#" output="filtersWDDX" />
		<cfset client[arguments.ui & ".filtersWDDX"] = filtersWDDX />
		<cfreturn filtersStruct />
	</cffunction>
	
	<cffunction name="filtersToQueryParams" access="public" returntype="struct">
		<cfargument name="filters" type="struct" required="yes" />
		<cfset var queryParams = StructNew() />
		<cfloop index="filter" list="#StructKeyList(arguments.filters)#">
			<cfif arguments.filters[filter] neq "" and arguments.filters[filter] neq "all">
				<cfset queryParams[filter] = arguments.filters[filter] />
			</cfif>
		</cfloop>
		<cfreturn queryParams />
	</cffunction>

	<!--- <cffunction name="getUIConfig" access="public" returntype="any" hint="Gets UI including filters, methods and soon, child objects and collections">
		<cfargument name="ui" type="string" required="yes" />
		<cfargument name="filter" type="any" required="no" default="false" hint="Optional boolean or struct - false (current or default), true (default) struct (override)" />
		<cfargument name="defaultFilter" type="any" required="no" default="false" hint="Optional struct of default filter" />
		<cfargument name="asJSON" type="boolean" required="no" default="false" />
		<cfscript>
			var ret = StructNew();
			ret["ui"] = arguments.ui;
			ret["defaultFilter"] = arguments.defaultFilter;
			ret["filter"] = getUIFilter(arguments.ui, arguments.filter, arguments.defaultFilter);
			ret["filtered"] = IIf(compareStructs(struct1 = ret.filter, struct2 = ret.defaultFilter), false, true);
			return ret;
		</cfscript>
	</cffunction>

	<cffunction name="getUIFilter" access="public" returntype="struct" hint="Creates, resets and updates UI filter">
		<cfargument name="ui" type="string" required="yes" />
		<cfargument name="filter" type="any" required="no" default="false" />
		<cfargument name="defaultFilter" type="any" required="no" default="false" />
		<cfscript>
			var currentFilter = "";
			if (StructKeyExists(client, arguments.ui & ".filterWDDX")) {
				currentFilter = wddxToCFML(client[arguments.ui & ".filterWDDX"]);
			}
			// if filter = true, set to default
			if (IsBoolean(arguments.filter) and arguments.filter eq true) {
				setUIFilter(arguments.ui, arguments.defaultFilter);
			// if filter is struct, set to filter
			} else if (IsStruct(arguments.filter)) {
				setUIFilter(arguments.ui, arguments.filter);
			// if filter = false, get current.  If no current, set to default
			} else if (IsBoolean(arguments.filter) and arguments.filter eq false) {
				if (NOT IsStruct(currentFilter)) {
					setUIFilter(arguments.ui, arguments.defaultFilter);
				}
			}
			return wddxToCFML(client[arguments.ui & ".filterWDDX"]);
		</cfscript>
	</cffunction> --->
	
	<cffunction name="getUIFilter" access="public" returntype="struct" hint="Returns user filter for ui">
		<cfargument name="ui" type="string" required="yes" />
		<cfargument name="defaultFilter" type="struct" required="yes" />
		<cfscript>
			if (StructKeyExists(client, arguments.ui & ".filterWDDX")) {
				return wddxToCFML(client[arguments.ui & ".filterWDDX"]);
			} else {
				return setUIFilter(arguments.ui, arguments.defaultFilter);
			}
		</cfscript>
	</cffunction>
	
	<cffunction name="setUIFilter" access="public" returntype="any" hint="Saves user filter for ui">
		<cfargument name="ui" type="string" required="yes" />
		<cfargument name="filter" type="struct" required="yes" />
		<cfscript>
			DeleteClientVariable(arguments.ui & ".filtersWDDX");
			DeleteClientVariable(arguments.ui & ".filterWDDX");
			client[arguments.ui & ".filterWDDX"] = cfmlToWDDX(arguments.filter);
			return arguments.filter;
		</cfscript>
	</cffunction>
	
	<cffunction name="compareStructs" access="public" returntype="boolean" hint="Shallow or key/value comparison of two structures. Returns true if they are equal.">
		<cfargument name="struct1" type="struct" required="yes" />
		<cfargument name="struct2" type="struct" required="yes" />
		<cfscript>
			//var ret = true;
			var struct1KeyCount = StructCount(arguments.struct1);
			var struct1KeyList = "";
			//var struct1ValueList = "";
			var struct2KeyCount = StructCount(arguments.struct2);
			var struct2KeyList = "";
			//var struct2ValueList = "";
			var key = "";
			if (struct1KeyCount neq struct2KeyCount) {
				return false;
			} else {
				struct1KeyList = ListSort(StructKeyList(arguments.struct1), "textnocase");
				struct2KeyList = ListSort(StructKeyList(arguments.struct2), "textnocase");
				if (struct1KeyList neq struct2KeyList) {
					return false;
				} else {
					for (key in arguments.struct1) {
						if (arguments.struct1[key] neq arguments.struct2[key]) {
							return false;
						}
					}
				}
			}
			return true;
		</cfscript>		
	</cffunction>

	<cffunction name="filterToQueryParams" access="public" returntype="struct">
		<cfargument name="filter" type="struct" required="yes" />
		<cfscript>
			var ret = StructNew();
			var key = "";
			var value = "";
			for (i in arguments.filter) {
				if (arguments.filter[key] eq "all") {
					value = "";	
				} else {
					value = arguments.filter[key];	
				}
				ret[key] = value;	
			}
			return ret;
		</cfscript>
	</cffunction>
	
	<!--- Object type conversion --->
	<cffunction name="wddxToCFML" access="public" returntype="any">
		<cfargument name="wddx" type="string" required="true" />
		<cfset var cfml = "" />
		<cfwddx action="wddx2cfml" input="#arguments.wddx#" output="cfml" />
		<cfreturn cfml />
	</cffunction>
	
	<cffunction name="cfmlToWDDX" access="public" returntype="any">
		<cfargument name="cfml" type="any" required="true" />
		<cfset var wddxOut = "" />
		<cfset var key = "" />
		<cfset var cfmlIn = arguments.cfml />
		<cfif IsStruct(arguments.cfml)>
			<cfloop item="key" collection="#arguments.cfml#">
				<cfset cfmlIn[key] = arguments.cfml[key] />
			</cfloop>
		</cfif>
		<cfwddx action="cfml2wddx" input="#cfmlIn#" output="wddxOut" />
		<cfreturn wddxOut />
	</cffunction>
	
	<cffunction name="deserializeWDDX" access="public" returntype="any">
		<cfargument name="wddx" required="yes" />
		<cfset var deserializedWDDX = "">
		<cfset var ret = StructNew() />
		<cfwddx action="wddx2cfml" input="#arguments.wddx#" output="deserializedWDDX" />
		<cfif IsQuery(deserializedWDDX)>
			<cfreturn queryToStruct(deserializedWDDX) />
		<cfelse>
			<cfloop index="key" list="#StructKeyList(deserializedWDDX)#">
				<cfset ret["#LCase(key)#"] = deserializedWDDX[key] />
			</cfloop>
			<cfreturn ret />
		</cfif>
	</cffunction>
	
	<cffunction name="queryToCSV" access="public" returntype="string" hint="I take a query and convert it to a comma separated value string.">
		<cfargument name="query" type="query" required="true" hint="I am the query being converted to CSV."	/>
		<cfargument name="fields" type="string" required="true" hint="I am the list of query fields to be used when creating the CSV value." />
		<cfargument name="createHeaderRow" type="boolean" required="false" default="true" hint="I flag whether or not to create a row of header values." />
		<cfargument name="delimiter" type="string" required="false" default="," hint="I am the field delimiter in the CSV value." />
		<!--- Define the local scope. --->
		<cfset var local = {} />
		<!---
		First, we want to set up a column index so that we can
		iterate over the column names faster than if we used a
		standard list loop on the passed-in list.
		--->
		<cfset local.columnNames = [] />
		<!---
		Loop over column names and index them numerically. We
		are working with an array since I believe its loop times
		are faster than that of a list.
		--->
		<cfloop index="local.columnName" list="#arguments.fields#" delimiters=",">
			<!--- Store the current column name. --->
			<cfset ArrayAppend(local.columnNames, Trim(local.columnName)) />
		</cfloop>
		<!--- Store the column count. --->
		<cfset local.columnCount = ArrayLen(local.columnNames) />
		<!---
		Now that we have our index in place, let's create
		a string buffer to help us build the CSV value more
		effiently.
		--->
		<cfset local.buffer = CreateObject("java", "java.lang.StringBuffer").Init() />
		<!--- Create a short hand for the new line characters. --->
		<cfset local.newLine = (chr(13) & chr(10)) />
		<!--- Check to see if we need to add a header row. --->
		<cfif arguments.createHeaderRow>
			<!--- Create array to hold row data. --->
			<cfset local.rowData = [] />
			<!--- Loop over the column names. --->
			<cfloop index="local.columnIndex" from="1" to="#local.columnCount#" step="1">
				<!--- Add the field name to the row data. --->
				<cfset local.rowData[local.columnIndex] = """#local.columnNames[ local.columnIndex ]#""" />
			</cfloop>
			 
			<!--- Append the row data to the string buffer. --->
			<cfset local.buffer.Append(JavaCast("string",(ArrayToList(local.rowData, arguments.delimiter) & local.newLine))) />
		</cfif>
		<!---
		Now that we have dealt with any header value, let's
		convert the query body to CSV. When doing this, we are
		going to qualify each field value. This is done be
		default since it will be much faster than actually
		checking to see if a field needs to be qualified.
		--->
		<!--- Loop over the query. --->
		<cfloop query="arguments.query">
			<!--- Create array to hold row data. --->
			<cfset local.rowData = [] />
			<!--- Loop over the columns. --->
			<cfloop index="local.columnIndex" from="1" to="#local.columnCount#" step="1">
				<!--- Add the field to the row data. --->
				<cfset local.rowData[local.columnIndex] = """#Replace(arguments.query[local.columnNames[local.columnIndex]][arguments.query.CurrentRow], """", """""", "all" )#""" />
			</cfloop>
			<!--- Append the row data to the string buffer. --->
			<cfset local.buffer.Append(JavaCast("string",(ArrayToList(local.rowData, arguments.delimiter) & local.newLine))) />
		</cfloop>
		<!--- Return the CSV value. --->
		<cfreturn local.buffer.ToString() />
	</cffunction>
	
	<cffunction name="queryToArray" access="public" returntype="any"
    hint="Converts CF query records to structs. Returns a single struct (1 record) or an array of structs (>1 record).">
		<cfargument name="Query" type="query" required="true" />
		<cfargument name="Row" type="numeric" required="false" default="0" />
		<cfscript>
			return queryToStruct(argumentCollection = arguments);
		</cfscript>
    </cffunction>
	
	<cffunction name="queryToStruct" access="public" returntype="any"
    hint="Converts CF query records to structs. Returns a single struct (1 record) or an array of structs (>1 record).">
		<cfargument name="Query" type="query" required="true" />
		<cfargument name="Row" type="numeric" required="false" default="0" />
		<cfscript>
			// Define the local scope.
			var LOCAL = StructNew();
			// Determine the indexes that we will need to loop over.
			// To do so, check to see if we are working with a given row,
			// or the whole record set.
			if (ARGUMENTS.Row){
				// We are only looping over one row.
				LOCAL.FromIndex = ARGUMENTS.Row;
				LOCAL.ToIndex = ARGUMENTS.Row;
			} else {
				// We are looping over the entire query.
				LOCAL.FromIndex = 1;
				LOCAL.ToIndex = ARGUMENTS.Query.RecordCount;
			}
			// Get the list of columns as an array and the column count.
			LOCAL.Columns = ListToArray( LCase(ARGUMENTS.Query.ColumnList) );
			LOCAL.ColumnCount = ArrayLen( LOCAL.Columns );
			// Create an array to keep all the objects.
			LOCAL.DataArray = ArrayNew( 1 );
			// Loop over the rows to create a structure for each row.
			for (LOCAL.RowIndex = LOCAL.FromIndex ; LOCAL.RowIndex LTE LOCAL.ToIndex ; LOCAL.RowIndex = (LOCAL.RowIndex + 1)){
				// Create a new structure for this row.
				ArrayAppend( LOCAL.DataArray, StructNew() );
				// Get the index of the current data array object.
				LOCAL.DataArrayIndex = ArrayLen( LOCAL.DataArray );
				// Loop over the columns to set the structure values.
				for (LOCAL.ColumnIndex = 1 ; LOCAL.ColumnIndex LTE LOCAL.ColumnCount ; LOCAL.ColumnIndex = (LOCAL.ColumnIndex + 1)){
					// Get the column value.
					LOCAL.ColumnName = LOCAL.Columns[ LOCAL.ColumnIndex ];
					// Set column value into the structure.
					if (IsWDDX(ARGUMENTS.Query[ LOCAL.ColumnName ][ LOCAL.RowIndex ])) {
						LOCAL.DataArray[ LOCAL.DataArrayIndex ][ LOCAL.ColumnName ] = deserializeWDDX(ARGUMENTS.Query[ LOCAL.ColumnName ][ LOCAL.RowIndex ]);
					} else if (ListLast(LOCAL.ColumnName, "_") eq "date") {
						LOCAL.DataArray[ LOCAL.DataArrayIndex ][ LOCAL.ColumnName ] = DateFormat(ARGUMENTS.Query[ LOCAL.ColumnName ][ LOCAL.RowIndex ], "m/d/yyyy");
					} else if (LOCAL.ColumnName contains "date_time") {
						LOCAL.DataArray[ LOCAL.DataArrayIndex ][ LOCAL.ColumnName ] = DateFormat(ARGUMENTS.Query[ LOCAL.ColumnName ][ LOCAL.RowIndex ], "m/d/yyyy") & " " & TimeFormat(ARGUMENTS.Query[ LOCAL.ColumnName ][ LOCAL.RowIndex ], "h:mmtt");
					} else if (ListLast(LOCAL.ColumnName, "_") eq "time") {
						LOCAL.DataArray[ LOCAL.DataArrayIndex ][ LOCAL.ColumnName ] = TimeFormat(ARGUMENTS.Query[ LOCAL.ColumnName ][ LOCAL.RowIndex ], "h:mmtt");
					} else {
						LOCAL.DataArray[ LOCAL.DataArrayIndex ][ LOCAL.ColumnName ] = ARGUMENTS.Query[ LOCAL.ColumnName ][ LOCAL.RowIndex ];
					}
				}
			}
			// At this point, we have an array of structure objects that
			// represent the rows in the query over the indexes that we
			// wanted to convert. If we did not want to convert a specific
			// record, return the array. If we wanted to convert a single
			// row, then return the just that STRUCTURE, not the array.
			if (ARGUMENTS.Row){
				// Return the first array item.
				return( LOCAL.DataArray[ 1 ] );
			} else {
				// Return the entire array.
				return( LOCAL.DataArray );
			}
			</cfscript>
	</cffunction>
	
	<cffunction name="lowercaseKeys" access="public" returntype="struct">
		<cfargument name="struct" type="struct" required="yes" />
		<cfscript>
			var ret = StructNew();
			var key = "";
			for (key in arguments.struct) {
				ret[LCase(key)] = arguments.struct[key];
			}
			return ret;
		</cfscript>
	</cffunction>
	
	<cffunction name="formatForJSON" access="public" returntype="struct">
		<cfargument name="struct" type="struct" required="yes" />
		<cfargument name="recurse" type="boolean" required="no" default="false" />
		<cfscript>
			var ret = StructNew();
			var key = "";
			var value = "";
			for (key in arguments.struct) {
				key = LCase(key);
				value = arguments.struct[key];
				ret[key] = value;
			}
			return ret;
		</cfscript>
	</cffunction>
	
	<cffunction name="isUserEmailUnique" access="public" returntype="boolean">
		<cfargument name="userID" type="numeric" required="yes" />
		<cfargument name="email" type="string" required="yes" />
		<cfset var isUnique = true />
		<cfinvoke component="assets.csac.com.gateway.UserGateway" method="query" returnvariable="users">
			<cfinvokeargument name="Email" value="#arguments.email#" />
		</cfinvoke>
		<cfloop query="#users#">
			<cfif userID neq User_ID>
				<cfset isUnique = false />
			</cfif>
		</cfloop>
		<cfreturn isUnique />
	</cffunction>

	<cffunction name="formatPhone" access="public" returntype="string">
		<cfargument name="phone" type="string" required="yes" />
		<cfset var fPhone = "">
		<cfset var ret = "">
		<cfset fPhone = Replace(arguments.phone, "(", "", "all")>
		<cfset fPhone = Replace(fPhone, ")", "", "all")>
		<cfset fPhone = Replace(fPhone, " ", "", "all")>
		<cfset fPhone = Replace(fPhone, "-", "", "all")>
		<cfset fPhone = Replace(fPhone, ".", "", "all")>
		<cfif len(fPhone) gte 10>
			<cfset fPhone = Right(fPhone, 10)>
			<cfset ret = Left(fPhone, 3) & "-" & Mid(fPhone, 4, 3) & "-" & Right(fPhone, 4)>
		</cfif>
		<cfreturn ret>
	</cffunction>
	
	<cffunction name="singleQuotifyList" access="public" returntype="string">
		<cfargument name="list" type="string" required="yes" />
		<cfset var ret = "">
		<cfloop index="item" list="#arguments.list#">
			<cfset ret = ListAppend(ret, "'#item#'", ",") />
		</cfloop>
		<cfreturn ret>
	</cffunction>
	
	<cffunction name="doubleQuotifyList" access="public" returntype="string">
		<cfargument name="list" type="string" required="yes" />
		<cfset var ret = "">
		<cfloop index="item" list="#arguments.list#">
			<cfset ret = ListAppend(ret, '"#item#"', ',') />
		</cfloop>
		<cfreturn ret>
	</cffunction>
	
	<cffunction name="formatShortDate" access="public" returntype="string">
		<cfargument name="dateVal" required="yes" />
		<cfif IsDate(arguments.dateVal)>
			<cfreturn DateFormat(arguments.dateVal, "m/d/yyyy") />
		<cfelse>
			<cfreturn "" />
		</cfif>
	</cffunction>

	<cffunction name="formatZipCode" access="public" returntype="string">
		<cfargument name="zipCode" type="string" required="yes" />
		<cfif NOT IsValid("zipcode", Trim(arguments.zipCode))>
			<cfreturn "" />
		<cfelse>
			<cfreturn Left(Trim(arguments.zipCode), 5) />
		</cfif>
	</cffunction>
	
	<cffunction name="toNumber" access="public" returntype="string">
		<cfargument name="string" type="string" required="yes" />
		<cfset var preString = Trim(arguments.string) />
		<cfset var postString = "" />
		<cfset var i = 1 />
		<cfset var character = "" />
		<cfloop index="i" from="1" to="#Len(preString)#">
			<cfset character = Mid(preString, i, 1) />
			<cfif IsNumeric(character)>
				<cfset postString = postString & character />
			</cfif>
		</cfloop>
		<cfreturn postString />
	</cffunction>
	
	<cffunction name="processTrigger" access="public" returntype="any">
		<cfargument name="trigger" type="string" required="yes" />
		<cfargument name="data" type="any" required="no" default="" />
		<cfscript>
			switch(arguments.trigger) {
				case "newOrderNotification":
					return emailNewOrderNotification(argumentCollection = arguments.data);
				break;
				case "approvedOrderNotification":
					return emailApprovedOrderNotification(argumentCollection = arguments.data);
				break;
				case "shippedOrderNotification":
					return emailShippedOrderNotification(argumentCollection = arguments.data);
				break;
			}
			return false;
		</cfscript>
	</cffunction>
	
	<cffunction name="emailPassword" access="public" returntype="boolean">
		<cfargument name="email" type="string">
		<cfset var user = "">
		<cfset var recipients = "">
		<cfset var subject = "">
		<cfset var message = "">
		<cfinvoke component="assets.csac.com.gateway.UserGateway" method="query" returnvariable="user">
			<cfinvokeargument name="Email" value="#email#" />
		</cfinvoke>
		<cfif user.RecordCount eq 1>
			<cfset recipients = "#user.Email#">
			<cfset subject = "Your Cash for College Workshop Management System Password">
			<cfoutput>
				<cfsavecontent variable="message">
					<p>#user.First_Name# #user.Last_Name#,</p>
					<p>Your Cash for College Workshop Management System password is &quot;#user.Password#&quot;.</p>
					<p><a href="https://www.calgrants.org/organizers">Login to your account</a></p>
					<p>If the link above does not work, copy and paste the following URL into your web browser:</p>
					<p>https://www.calgrants.org/organizers</p>
					<p>Please delete this e-mail message now to protect your password.</p>
				</cfsavecontent>
			</cfoutput>
			<cfreturn sendEmail(recipients, subject, message, "HTML")>
		<cfelse>
			<cfreturn false />
		</cfif>
	</cffunction>
	
	<!--- <cffunction name="emailUserApproval" access="public" returntype="boolean">
		<cfargument name="userID" type="numeric" />
		<cfset var user = "" />
		<cfset var organization = "" />
		<cfset var recipients = "" />
		<cfset var subject = "" />
		<cfset var message = "" />
		<cfinvoke component="assets.csac.com.gateway.UserGateway" method="get" returnvariable="user">
			<cfinvokeargument name="User_ID" value="#userID#" />
		</cfinvoke>
		<cfinvoke component="assets.csac.com.gateway.OrganizationGateway" method="get" returnvariable="organization">
			<cfinvokeargument name="Organization_ID" value="#user.getOrganization_ID()#" />
		</cfinvoke>
		<cfset recipients = user.getEmail() />
		<cfset subject = "Your Cash for College account has been approved" />
		<cfoutput>
			<cfsavecontent variable="message">
				<p>#user.getFirst_Name()# #user.getLast_Name()#,</p>
				<p>Your Cash for College Workshop Management System (WMS) account for #organization.getOrganization()# has been approved.</p>
				<p><a href="https://www.calgrants.org/organizers">Login to your account</a></p>
				<p>If the link above does not work, copy and paste the following URL into your web browser:</p>
				<p>https://www.calgrants.org/organizers</p>
				<p>If you have forgotten your password, click the &quot;Forgot your password?&quot; link on the login page.</p>
			</cfsavecontent>
		</cfoutput>
		<cfreturn sendEmail(recipients, subject, message, "HTML")>
	</cffunction> --->
	
	<cffunction name="emailStockWarning" access="public">
		<cfargument name="Product_ID" type="numeric" required="yes">
		<cfscript>
			var productGateway = new assets.csac.com.gateway.ProductGateway();
			var product = productGateway.get(Product_ID = arguments.Product_ID);
			var productStock = productGateway.getStock(productIDs = arguments.Product_ID);
			
			var recipients = "kenhobbs@gmail.com";
			var subject = "CSAC Publications Low Stock Warning";
			var message = "";
		</cfscript>
		<cfsavecontent variable="message">
			<h1>Low Stock Warning</h1>
			<h2>#product.getProduct()#</h2>
			<cfdump var="#productStock#" label="Inventory Details">
		</cfsavecontent>
		<cfreturn sendEmail(recipients = recipients, subject = subject, message = message)>
	</cffunction>

	<cffunction name="emailOrderNew" access="public">
		<cfargument name="Order_ID" type="numeric" required="yes">
		<cfscript>
			var orderGateway = new assets.csac.com.gateway.OrderGateway();
			var orderProductGateway = new assets.csac.com.gateway.OrderProductGateway();
			var order = orderGateway.get(Order_ID = argument.Order_ID);
			var orderProducts = orderProductGateway.query(Order_ID = order.getOrder_ID());
			
			var recipients = order.getEmail();
			var subject = "CSAC Publications Order ###arguments.Order_ID# Recieved";
			var message = "";
		</cfscript>
		<cfsavecontent variable="message">
			<h1>CSAC Publications Order ###order.getOrder_ID()# Received</h1>
			<cfdump var="#order#" label="Order">
			<cfdump var="#orderProducts#" label="Items">
		</cfsavecontent>
		<cfreturn sendEmail(recipients = recipients, subject = subject, message = message)>
	</cffunction>
	
	<cffunction name="emailOrderApproved" access="public" returntype="boolean">
		<cfargument name="Order_ID" type="numeric" required="yes">
		<cfscript>
			var orderGateway = new assets.csac.com.gateway.OrderGateway();
			var orderProductGateway = new assets.csac.com.gateway.OrderProductGateway();
			var order = orderGateway.get(Order_ID = argument.Order_ID);
			var orderProducts = orderProductGateway.query(Order_ID = order.getOrder_ID());
			
			var recipients = order.getEmail();
			var subject = "CSAC Publications Order ###order.getOrder_ID# Approved";
			var message = "";
		</cfscript>
		<cfsavecontent variable="message">
			<h1>CSAC Publications Order ###order.getOrder_ID()# Approved</h1>
			<cfdump var="#order#" label="Order">
			<cfdump var="#orderProducts#" label="Items">
		</cfsavecontent>
		<cfreturn sendEmail(recipients = recipients, subject = subject, message = message)>
	</cffunction>
	
	<cffunction name="emailOrderShipped" access="public" returntype="boolean">
		<cfargument name="Order_ID" type="numeric" required="yes">
		<cfscript>
			var orderGateway = new assets.csac.com.gateway.OrderGateway();
			var orderProductGateway = new assets.csac.com.gateway.OrderProductGateway();
			var trackingGateway = new assets.csac.com.gateway.TrackingGateway();
			var order = orderGateway.get(Order_ID = argument.Order_ID);
			var orderProducts = orderProductGateway.query(Order_ID = order.getOrder_ID());
			var trackings = trackingGateway.query(Order_ID = order.getOrder_ID());
			var recipients = order.getEmail();
			var subject = "CSAC Publications Order ###order.getOrder_ID()# Shipped";
			var message = "";
		</cfscript>
		<cfsavecontent variable="message">
			<h1>CSAC Publications Order ###order.getOrder_ID()# Shipped</h1>
			<cfdump var="#order#" label="Order">
			<cfdump var="#orderProducts#" label="Items">
			<cfdump var="#trackings#" label="Tracking">
		</cfsavecontent>
		<cfreturn sendEmail(recipients = recipients, subject = subject, message = message)>
	</cffunction>
	
	<cffunction name="sendEmail" access="public" returntype="boolean">
		<cfargument name="recipients" type="string" required="yes" hint="Comma separated list of email addresses" />
		<cfargument name="subject" type="string" required="yes" />
		<cfargument name="message" type="string" required="yes" />
		<cfargument name="from" type="string" required="no" default="foo" />
		<cfargument name="replyTo" type="string" required="no" default="foo" />
		<cfargument name="emailType" type="string" required="no" default="HTML" />
		<cfscript>
			var mode = getMode();
			var recipient = "";
			var args = Duplicate(arguments);
			if (NOT IsValid("email", args.from)) {
				args.from = "publications@csac.ca.gov";	
			}
			if (NOT IsValid("email", args.replyTo)) {
				args.replyTo = args.from;	
			}
			if (mode eq "dev") {
				args.recipients = "kenhobbs@gmail.com";
				args.cc = "";
				args.bcc = "";
				args.subject = "Dev Email - " & args.subject;
			} else {
				args.cc = "";
				args.bcc = "publications@csac.ca.gov";
			}
		</cfscript>
		<cftry>
			<cfloop index="recipient" list="#args.recipients#">
				<!--- <cfmail to="#recipient#" bcc="#args.bcc#" from="#args.from#" replyto="#args.replyTo#" subject="#args.subject#" type="HTML">
					#args.message#
				</cfmail>
				<cfif mode eq "dev">
					<cflog text="#args.message#" file="MailLogger" type="information">
				</cfif> --->
			</cfloop>
			<cfcatch type="any">
				<cfreturn false />
			</cfcatch>
		</cftry>
		<cfreturn true>
	</cffunction>
	
	<!--- <cffunction name="sendEmail" access="public" returntype="boolean">
		<cfargument name="recipients" type="string" required="yes" hint="One email or a comma-separated list of emails">
		<cfargument name="subject" type="string" required="yes">
		<cfargument name="message" type="string" required="yes">
		<cfargument name="emailType" type="string" required="no" default="HTML">
		<cfset local.result = true />
		<cfset local.recipients = arguments.recipients />
		<cfset local.bcc = request.developer.email />
		<cfset local.from = request.personality.email />
		<cfset local.replyTo = request.personality.email />
		<cfif getMode() eq "dev">
			<cfset local.recipients = request.developer.email>
			<cfset local.bcc = "">
		</cfif>
		<cftry>
			<cfloop index="recipient" list="#local.recipients#">
				<cfmail to="#recipient#" bcc="#local.bcc#" from="#local.from#" replyto="#local.replyTo#" subject="#arguments.subject#" type="#arguments.emailType#">
					<cfif arguments.emailType eq "HTML">
						#arguments.message#
					<cfelseif arguments.emailType eq "Plain Text">
						#arguments.message#
					</cfif>
				</cfmail>
				<cfif getMode() eq "dev">
					<cflog text="#arguments.message#" file="MailLogger" type="information">
				</cfif>
			</cfloop>
			<cfcatch type="any">
				<cfset local.result = false />
			</cfcatch>
		</cftry>
		<cfreturn local.result>
	</cffunction> --->

	<cffunction name="getRoles" returntype="array">
		<cfset var roles = ArrayNew(1)>
		<cfset roles[1] = StructNew()>
		<cfset roles[1]["text"] = "Administrator">
		<cfset roles[1]["value"] = "Administrator">
		<cfset roles[1]["isAdminRole"] = true>
		<cfset roles[2] = StructNew()>
		<cfset roles[2]["text"] = "Manager">
		<cfset roles[2]["value"] = "Manager">
		<cfset roles[2]["isAdminRole"] = true>
		<cfset roles[3] = StructNew()>
		<cfset roles[3]["text"] = "Shipper">
		<cfset roles[3]["value"] = "Shipper">
		<cfset roles[3]["isAdminRole"] = false>
		<cfset roles[4] = StructNew()>
		<cfset roles[4]["text"] = "Customer">
		<cfset roles[4]["value"] = "Customer">
		<cfset roles[4]["isAdminRole"] = false>
		<cfreturn roles />
	</cffunction>
	
	<cffunction name="getOrderStatuses" returntype="array">
		<cfset var orderStatuses = ArrayNew(1)>
		<cfset orderStatuses[1] = StructNew()>
		<cfset orderStatuses[1]["text"] = "New">
		<cfset orderStatuses[1]["value"] = "New">
		<cfset orderStatuses[1]["trigger"] = "newOrderNotification">
		<cfset orderStatuses[2] = StructNew()>
		<cfset orderStatuses[2]["text"] = "On Hold/Under Review">
		<cfset orderStatuses[2]["value"] = "On Hold">
		<cfset orderStatuses[2]["trigger"] = "onHoldOrderNotification">
		<cfset orderStatuses[3] = StructNew()>
		<cfset orderStatuses[3]["text"] = "Approved">
		<cfset orderStatuses[3]["value"] = "Approved">
		<cfset orderStatuses[3]["trigger"] = "approvedOrderNotification">
		<cfset orderStatuses[4] = StructNew()>
		<cfset orderStatuses[4]["text"] = "Staged">
		<cfset orderStatuses[4]["value"] = "Staged">
		<cfset orderStatuses[4]["trigger"] = "stagedOrderNotification">
		<cfset orderStatuses[5] = StructNew()>
		<cfset orderStatuses[5]["text"] = "Shipped">
		<cfset orderStatuses[5]["value"] = "Shipped">
		<cfset orderStatuses[5]["trigger"] = "shippedOrderNotification">
		<cfset orderStatuses[6] = StructNew()>
		<cfset orderStatuses[6]["text"] = "Cancelled">
		<cfset orderStatuses[6]["value"] = "Cancelled">
		<cfset orderStatuses[6]["trigger"] = "cancelledOrderNotification">
		<cfreturn orderStatuses />
	</cffunction>
	
	<cffunction name="getOrderStatus" returntype="string">
		<cfargument name="args" type="struct" required="yes" hint="An order submission" />
		<cfset var orderStatus = "" />
		<cfscript>
			if (arguments.args.Order_ID eq 0) {
				orderStatus = "New";
			}
		</cfscript>
		<cfreturn orderStatus />
	</cffunction>
	
	<cffunction name="getInstitutionTypes" returntype="array">
		<cfset var institutionTypes = ArrayNew(1)>
		<cfset institutionTypes[1] = StructNew()>
		<cfset institutionTypes[1]["text"] = "High School">
		<cfset institutionTypes[1]["value"] = "High School">
		<cfset institutionTypes[2] = StructNew()>
		<cfset institutionTypes[2]["text"] = "College/University">
		<cfset institutionTypes[2]["value"] = "College University">
		<cfset institutionTypes[3] = StructNew()>
		<cfset institutionTypes[3]["text"] = "Other">
		<cfset institutionTypes[3]["value"] = "Other">
		<cfreturn institutionTypes />
	</cffunction>
	
	<cffunction name="getOrigins" returntype="array">
		<cfset var origins = ArrayNew(1)>
		<cfset origins[1] = StructNew()>
		<cfset origins[1]["text"] = "CSAC">
		<cfset origins[1]["value"] = "CSAC">
		<cfset origins[2] = StructNew()>
		<cfset origins[2]["text"] = "CFC">
		<cfset origins[2]["value"] = "CFC">
		<cfreturn origins />
	</cffunction>
	
	<cffunction name="getProductFiles" returntype="query">
		<cfargument name="productIDs" type="string" required="yes" />
		<cfset var productFiles = QueryNew("Product_ID, File_Type, Attributes, DateLastModified, Directory, Mode, Name, Size, Type", "Integer, VarChar, VarChar, Date, Varchar, Varchar, Varchar, Integer, Varchar") />
		<cfset var pID = "" />
		<cfset var files = "" />
		<cfloop index="pID" list="#arguments.productIDs#">
			<cfif getMode() eq "dev">
				<cfdirectory action="list" directory="C:\inetpub\wwwroot\csac\common\files\products\" name="files" filter="#pID#.*" />
			<cfelse>
				<cfdirectory action="list" directory="D:\virtuals\publications.csac.ca.gov\common\files\products\" name="files" filter="#pID#.*" />
			</cfif>
			<cfloop query="files">
				<cfset QueryAddRow(productFiles, 1) />
				<cfset QuerySetCell(productFiles, "Product_ID", pID) />
				<cfset QuerySetCell(productFiles, "File_Type", "file") />
				<cfset QuerySetCell(productFiles, "Attributes", Attributes) />
				<cfset QuerySetCell(productFiles, "DateLastModified", DateLastModified) />
				<cfset QuerySetCell(productFiles, "Directory", Directory) />
				<cfset QuerySetCell(productFiles, "Mode", Mode) />
				<cfset QuerySetCell(productFiles, "Name", Name) />
				<cfset QuerySetCell(productFiles, "Size", Size) />
				<cfset QuerySetCell(productFiles, "Type", Type) />
			</cfloop>
		</cfloop>
		<cfreturn productFiles />
	</cffunction>
	
	<cffunction name="getProductImages" returntype="query">
		<cfargument name="productIDs" type="string" required="yes" />
		<cfset var productImages = QueryNew("Product_ID, File_Type, Attributes, DateLastModified, Directory, Mode, Name, Size, Type", "Integer, VarChar, VarChar, Date, Varchar, Varchar, Varchar, Integer, Varchar") />
		<cfset var pID = "" />
		<cfset var images = "" />
		<cfloop index="pID" list="#arguments.productIDs#">
			<cfif getMode() eq "dev">
				<cfdirectory action="list" directory="C:\inetpub\wwwroot\csac\common\images\products\" name="images" filter="#pID#.*" />
			<cfelse>
				<cfdirectory action="list" directory="D:\virtuals\publications.csac.ca.gov\common\images\products\" name="images" filter="#pID#.*" />
			</cfif>
			<cfloop query="images">
				<cfset QueryAddRow(productImages, 1) />
				<cfset QuerySetCell(productImages, "Product_ID", pID) />
				<cfset QuerySetCell(productImages, "File_Type", "image") />
				<cfset QuerySetCell(productImages, "Attributes", Attributes) />
				<cfset QuerySetCell(productImages, "DateLastModified", DateLastModified) />
				<cfset QuerySetCell(productImages, "Directory", Directory) />
				<cfset QuerySetCell(productImages, "Mode", Mode) />
				<cfset QuerySetCell(productImages, "Name", Name) />
				<cfset QuerySetCell(productImages, "Size", Size) />
				<cfset QuerySetCell(productImages, "Type", Type) />
			</cfloop>
		</cfloop>
		<cfreturn productImages />
	</cffunction>
	
	<!--- <cffunction name="getProductQuantityRules" returntype="any" hint="Returns min, max and increment quantity for a products. Includes inventory, packaging and custom rules (if applicable)">
		<cfargument name="productIDs" type="string" required="no" default="" />
		<cfscript>
			var productID = "";
			// get from csac
			var productGateway = new assets.csac.com.gateway.ProductGateway();
			var productsStock = productGateway.getStock(argumentCollection = arguments);
			for (productID in productsStock) {
				var productRule = "";
				var productMin = "";
				var productMax = "";
				// custom rules
				if (1 eq 1) {
					productRule = "Maximum 2000";
					productMin = 0;
					productMax = 2000;
				}
				if (Len(Trim(productRule)) > 0 && productMax < productsStock[productID].Maximum) {
					productsStock[productID].Rule = ListAppend(productsStock[productID].Rule, "Custom");
					productsStock[productID].Custom = productRule;
					productsStock[productID].Maximum = productMax;
				}
			}
			return productsStock;
		</cfscript>
	</cffunction> --->

    <cffunction name="toVoArray" access="public" returntype="array" output="true">
        <cfargument name="voName" type="string" required="true"/>
        <cfargument name="query" type="query" required="true"/>
        <cfscript>
            var voArray = arrayNew(1);
            var vo = "";
            var column = "";
            var loopCount = 0;
        </cfscript>
        <cfloop query="arguments.query">
            <cfset loopCount = loopCount + 1/>
            <cfset vo = structNew()/>
            <cfloop list="#arguments.query.ColumnList#" index="column">
                <cfset vo[column] = arguments.query[column][loopCount]/>
            </cfloop>

            <cfset arrayAppend(voArray, createObject(arguments.voName).init(argumentcollection=vo))/>
        </cfloop>
        <cfreturn voArray />
    </cffunction>

    <cffunction name="toVo" access="public" returntype="any" >
        <cfargument name="voName" type="string" required="true"/>
        <cfargument name="query" type="query" required="true"/>
        <cfset var vo = structNew()/>
        <cfset var column = '' />
        <cfloop list="#arguments.query.ColumnList#" index="column">
            <cfset vo[column] = arguments.query[column][1]/>
        </cfloop>
        <cfreturn createObject(arguments.voName).init(argumentcollection=vo) />
    </cffunction>

</cfcomponent>